2007-2015 Bryan O'Sullivan
License: BSD-3-clause
+Files: Utility/FileIO/CloseOnExec.hs
+Copyright: 2025 Joey Hess <id@joeyh.name>
+ 2024 Julian Ospald
+License: BSD-3-clause
+
Files: Utility/Matcher.hs Utility/Tor.hs Utility/Yesod.hs
Copyright: © 2010-2023 Joey Hess <id@joeyh.name>
License: AGPL-3+
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
- 3. Neither the name of the author nor the names of his contributors
+ 3. Neither the name of the author nor the names of other contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
.
{- This is a subset of the functions provided by file-io.
+ -
+ - All exported functions set the close-on-exec flag.
-
- When not building with file-io, this provides equvilant
- - RawFilePath versions.
+ - RawFilePath versions. Note that those versions do not currently
+ - set the close-on-exec flag.
-
- Since Prelude exports many of these as well, this needs to be imported
- qualified.
#ifdef WITH_OSPATH
#ifndef mingw32_HOST_OS
-import System.File.OsPath
+import Utility.FileIO.CloseOnExec
#else
-- On Windows, System.File.OsPath does not handle UNC-style conversion itself,
-- so that has to be done when calling it. See
import Utility.OsPath
import System.IO (IO, Handle, IOMode)
import Prelude (return)
-import qualified System.File.OsPath as O
+import qualified Utility.FileIO.CloseOnExec as O
import qualified Data.ByteString as B
import Control.Applicative
#endif
#else
--- When not building with OsPath, export RawFilePath versions
--- instead.
+-- RawFilePath versions
import Utility.OsPath
import Utility.FileSystemEncoding
import System.IO (IO, Handle, IOMode)
--- /dev/null
+{- This is a subset of the functions provided by file-io.
+ - All functions have been modified to set the close-on-exec
+ - flag to True.
+ -
+ - Copyright 2025 Joey Hess <id@joeyh.name>
+ - Copyright 2024 Julian Ospald
+ -
+ - License: BSD-3-clause
+ -}
+
+{-# OPTIONS_GHC -fno-warn-tabs #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Utility.FileIO.CloseOnExec
+(
+#ifdef WITH_OSPATH
+ withFile,
+ withFile',
+ openFile,
+ withBinaryFile,
+ openBinaryFile,
+ readFile,
+ readFile',
+ writeFile,
+ writeFile',
+ appendFile,
+ appendFile',
+ openTempFile,
+#endif
+) where
+
+#ifdef WITH_OSPATH
+
+import System.File.OsPath.Internal (withOpenFile', augmentError)
+import qualified System.File.OsPath.Internal as I
+import System.IO (IO, Handle, IOMode(..))
+import System.OsPath (OsPath, OsString)
+import Prelude (Bool(..), pure, either, (.), (>>=), ($))
+import Control.Exception
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+#ifndef mingw32_HOST_OS
+import System.Posix.IO
+#endif
+
+closeOnExec :: Bool
+closeOnExec = True
+
+withFile :: OsPath -> IOMode -> (Handle -> IO r) -> IO r
+withFile osfp iomode act = (augmentError "withFile" osfp
+ $ withOpenFile' osfp iomode False False closeOnExec (try . act) True)
+ >>= either ioError pure
+
+withFile'
+ :: OsPath -> IOMode -> (Handle -> IO r) -> IO r
+withFile' osfp iomode act = (augmentError "withFile'" osfp
+ $ withOpenFile' osfp iomode False False closeOnExec (try . act) False)
+ >>= either ioError pure
+
+openFile :: OsPath -> IOMode -> IO Handle
+openFile osfp iomode = augmentError "openFile" osfp $
+ withOpenFile' osfp iomode False False closeOnExec pure False
+
+withBinaryFile :: OsPath -> IOMode -> (Handle -> IO r) -> IO r
+withBinaryFile osfp iomode act = (augmentError "withBinaryFile" osfp
+ $ withOpenFile' osfp iomode True False closeOnExec (try . act) True)
+ >>= either ioError pure
+
+openBinaryFile :: OsPath -> IOMode -> IO Handle
+openBinaryFile osfp iomode = augmentError "openBinaryFile" osfp $
+ withOpenFile' osfp iomode True False closeOnExec pure False
+
+readFile :: OsPath -> IO BSL.ByteString
+readFile fp = withFile' fp ReadMode BSL.hGetContents
+
+readFile'
+ :: OsPath -> IO BS.ByteString
+readFile' fp = withFile fp ReadMode BS.hGetContents
+
+writeFile :: OsPath -> BSL.ByteString -> IO ()
+writeFile fp contents = withFile fp WriteMode (`BSL.hPut` contents)
+
+writeFile'
+ :: OsPath -> BS.ByteString -> IO ()
+writeFile' fp contents = withFile fp WriteMode (`BS.hPut` contents)
+
+appendFile :: OsPath -> BSL.ByteString -> IO ()
+appendFile fp contents = withFile fp AppendMode (`BSL.hPut` contents)
+
+appendFile'
+ :: OsPath -> BS.ByteString -> IO ()
+appendFile' fp contents = withFile fp AppendMode (`BS.hPut` contents)
+
+{- Unlike all other functions in this module, this only sets the
+ - close-on-exec flag after opening the file. Thus, it is vulnerable to
+ - races.
+ -
+ - Re-implementing openTempFile is difficult due to the current
+ - structure of file-io. See this issue for discussion about improving
+ - that: https://github.com/haskell/file-io/issues/44
+ - -}
+openTempFile :: OsPath -> OsString -> IO (OsPath, Handle)
+openTempFile tmp_dir template = do
+ (p, h) <- I.openTempFile tmp_dir template
+#ifndef mingw32_HOST_OS
+ fd <- handleToFd h
+ setFdOption fd CloseOnExec True
+ h' <- fdToHandle fd
+ pure (p, h')
+#else
+ pure (p, h)
+#endif
+
+#endif